using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
using Pavilion;
using Pavilion.GameTools;

namespace Pavilion.Locks
{
    public enum AltPerform
    {
        GetCurrentAlt,
        SetCurrentAlt,
        GetTargetAlt
    }

    public class AltitudeLock : ILock
    {
        public TargetTool.Alt Altitude { get; set; }
        public AltPerform AltPerform { get; set; }
        public string GameItemName { get; set; }

        public AltitudeLock()
        {
            Altitude = TargetTool.Alt.High;
            AltPerform = AltPerform.GetCurrentAlt;
        }

        public ILock Clone()
        {
            return new AltitudeLock()
            {
                GameItemName = this.GameItemName,
                Altitude = this.Altitude,
                AltPerform = this.AltPerform,
            };
        }

        public void Deserialize(XElement lockElement, string resourceName)
        {
            this.Altitude = (TargetTool.Alt)Enum.Parse(typeof(TargetTool.Alt), lockElement.Attribute("Altitude").Value);
            this.AltPerform = (AltPerform)Enum.Parse(typeof(AltPerform), lockElement.Attribute("AltPerform").Value);
        }

        public XElement Serialize()
        {
            XElement lockElement = new XElement("Lock");

            lockElement.Add(new XAttribute("Altitude", Altitude));
            lockElement.Add(new XAttribute("AltPerform", AltPerform));

            return lockElement;
        }

        public LockMonitorBase CreateLockMonitor(GameItem gameItem)
        {
            return new AltitudeLockMonitor(this);
        }

        public override string ToString()
        {
            return this.GetType().Name;
        }

        private class AltitudeLockMonitor : LockMonitorBase
        {
            public override ILock Lock { get { return altitudeLock; } }

            private AltitudeLock altitudeLock;
            private TargetTool target;

            public AltitudeLockMonitor(AltitudeLock altitudeLock)
            {
                this.altitudeLock = altitudeLock;
                mouseAltitudeChanged = new Action<TargetTool.Alt>(Target_MouseAltitudeChanged);
            }

            public override void Dispose()
            {
                Deactivate();

                this.target = null;
            }

            public override void Load(GameItem gameItem)
            {
                this.target = gameItem.ObtainTool<TargetTool>();

                Activate();
            }

            private Action<TargetTool.Alt> mouseAltitudeChanged;

            private void Activate()
            {
                if (altitudeLock.AltPerform == AltPerform.SetCurrentAlt)
                {
                    target.MouseAltitudeChanged += mouseAltitudeChanged;
                    UpdateLockStatusBySetCurrentAlt();
                }
                else if (altitudeLock.AltPerform == AltPerform.GetCurrentAlt)
                {
                    target.CharacterAltitudeChanged += new Action<TargetTool.Alt>(Target_CharacterAltitudeChanged);
                    UpdateLockStatusByGetCurrentAlt();
                }
                else
                    throw new Exception("The operation is not implemented.");
            }

            private void Deactivate()
            {
                if (altitudeLock.AltPerform == AltPerform.SetCurrentAlt)
                    target.MouseAltitudeChanged -= mouseAltitudeChanged;
                else if (altitudeLock.AltPerform == AltPerform.GetCurrentAlt)
                {
                    target.CharacterAltitudeChanged -= new Action<TargetTool.Alt>(Target_CharacterAltitudeChanged);
                    UpdateLockStatusByGetCurrentAlt();
                }
                else
                    throw new Exception("The operation is not implemented.");
            }

            public override void UpdateLock()
            {
                if (altitudeLock.AltPerform == AltPerform.SetCurrentAlt)
                    UpdateLockStatusBySetCurrentAlt();
                else if (altitudeLock.AltPerform == AltPerform.GetCurrentAlt)
                    UpdateLockStatusByGetCurrentAlt();
                else
                    throw new Exception("The operation is not implemented.");
            }

            private void UpdateLockStatusBySetCurrentAlt()
            {
                if (target == null)
                    return;

                if (altitudeLock.Altitude == target.MouseAltitude)
                    target.CharacterAltitude = altitudeLock.Altitude;

                IsOpen = (altitudeLock.Altitude == target.MouseAltitude);
            }

            private void UpdateLockStatusByGetCurrentAlt()
            {
                if (target == null)
                    return;

                IsOpen = (altitudeLock.Altitude == target.CharacterAltitude);
            }

            private void Target_MouseAltitudeChanged(TargetTool.Alt mouseAltitude)
            {
                UpdateLockStatusBySetCurrentAlt();
            }

            private void Target_CharacterAltitudeChanged(TargetTool.Alt characterAltitude)
            {
                UpdateLockStatusByGetCurrentAlt();
            }

            public override string ToString()
            {
                return this.GetType().Name;
            }
        }
    }
}
